Imports System.Collections.Generic
Imports System.Text
Imports System.Threading

Namespace Core
	Friend Class RecordHolder
		Friend mFile As ClusteredFile
        Private MyRecordNo As Int32
		Private mRecordWeakRef As WeakReference
		Private mReadBuffer As Byte()
		Private mWriteBuffer As Byte()
		Private ewh As EventWaitHandle = Nothing
		Friend RecordFinalized As New EventWaitHandle(False, EventResetMode.ManualReset)

		Private mHold As Object
        Public Sub New(file As ClusteredFile, RecordNo As Int32, record As Record)
            'RecordFinalized = new EventWaitHandle(false, EventResetMode.ManualReset);
#If DUMP_FINALIZE Then
			System.Diagnostics.Trace.WriteLine(file.mOriginalFile & " New RecordHolder for #" & RecordNo)
			System.Diagnostics.Debug.Assert(record IsNot Nothing)
#End If

            mFile = file
            MyRecordNo = RecordNo
            MakeWeakRef(record)
            mHold = record
        End Sub
        Public ReadOnly Property RecordNo() As Int32
            Get
                Return MyRecordNo
            End Get
        End Property
        Public Property Hold() As Boolean
            Get
                Return mHold IsNot Nothing
            End Get
            Set(value As Boolean)
                mHold = (If(Value, Record, Nothing))
            End Set
        End Property

		Private Sub MakeWeakRef(record As Record)
			mRecordWeakRef = New WeakReference(record, False)
		End Sub

		Public Property Record() As Record
			Get
				If mRecordWeakRef Is Nothing Then
					Return Nothing
				End If
                Dim result As Record = TryCast(mRecordWeakRef.Target, Record)
				Return result
			End Get
			Set
				' We could be tempted to write
				' mRecordWeakRef.Target=value 
				' but this is not advisable as this property
				' could be call from a GC destructor.
				MakeWeakRef(value)
			End Set
		End Property

		Friend Sub OnRecordDisposed(record As Record)
			Try
				' first we write the buffer 
				mFile.InternalFillWriteBuffer(Me, record)
			Finally
				' This is one of the only place where we use Threading mechanism.
				' The mainthread might be waiting for it
				RecordFinalized.[Set]()
			End Try
		End Sub

		Friend Function GetCurrentBuffer(readIfNeeded As Boolean) As Byte()
			If mWriteBuffer IsNot Nothing Then
				Return mWriteBuffer
			End If
			If mReadBuffer IsNot Nothing Then
				Return mReadBuffer
			End If

			If readIfNeeded Then
				System.Diagnostics.Debug.Assert(mFile.mRecordWidth >= 0, "RecordWidth should be bigger than 0.")
				mReadBuffer = New Byte(mFile.mRecordWidth - 1) {}
                mFile.InternalReadRecordFromDisk(MyRecordNo, mReadBuffer)
				Return mReadBuffer
			Else
				Return Nothing
			End If
		End Function

		Friend Function IsModified() As Boolean
			Return (mWriteBuffer IsNot Nothing)
		End Function

		Friend Sub SetNewBuffer(newBuffer As [Byte]())
			Dim modified As Boolean = False
			If mReadBuffer Is Nothing Then
				modified = True
			Else
				Dim len As Integer = mFile.mRecordWidth
				For i As Integer = 0 To len - 1
					If mReadBuffer(i) <> newBuffer(i) Then
						modified = True
						Exit For
					End If
				Next
			End If
			If modified Then
                Dim currentBuffer As Byte() = (If(mWriteBuffer IsNot Nothing, mWriteBuffer, mReadBuffer))
                ' we could clear the readBffer here to save memory
                mFile.OnWriteBufferModified(MyRecordNo, currentBuffer, newBuffer)
				mWriteBuffer = newBuffer
			End If

		End Sub

		Friend Sub Save()
			If mWriteBuffer IsNot Nothing Then
                mFile.InternalSaveRecordToDisk(MyRecordNo, mWriteBuffer)
				mWriteBuffer = Nothing
			End If
		End Sub

		Friend Sub SaveChanges(record As Record)
			mFile.InternalFillWriteBuffer(Me, record)
		End Sub

		Friend Overridable Function GetRecordField(record As Record, i As Integer) As Object
			Dim result As Object
            Dim table As DbfFile = DirectCast(mFile, DbfFile)
            Dim column As ColumnDefinition = table.mColumns(i)
			If column.mFieldInfo Is Nothing Then
				' Is there much benefit to emit this code?
                result = column.readXXX.Invoke(Nothing, New Object() {mFile, GetCurrentBuffer(True), column.ExtendedProperties("OffSet"), column.ExtendedProperties("ColumnWidth")})
			Else
				' Is there much benefit to emit this code?
				result = column.mFieldInfo.GetValue(record)
			End If
			If column.IsMemo() Then
                Dim memoRecordNo As Int32
                If result IsNot Nothing AndAlso Int32.TryParse(result.ToString(), memoRecordNo) Then
                    Dim memoValue As [Byte]() = table.mMemoFile.GetMemoValue(memoRecordNo)
                    If memoValue Is Nothing Then
                        memoValue = New Byte() {}
                    End If
                    If CType(column.ExtendedProperties("ColumnType"), ColumnType) = ColumnType.DBASE3_MEMO Then
                        result = table.mEncoding.GetString(memoValue)
                    Else
                        result = memoValue
                    End If
                End If
			End If
			Return result
		End Function
	End Class
End Namespace
